package ltd.nullpointer.tcp.core;

import com.boot2.core.utils.ReflectUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import ltd.nullpointer.tcp.core.annotation.TcpUpMessage;
import ltd.nullpointer.tcp.core.constant.TCPEnum;
import ltd.nullpointer.tcp.core.message.NPiotTCPMessage;
import ltd.nullpointer.tcp.core.resolver.AbstractTCPMessageClassFieldEntry.ClassFieldEntry;
import ltd.nullpointer.tcp.core.resolver.NPiotTCPMessageResolver;
import ltd.nullpointer.tcp.core.resolver.MessageResolverFactory;
import ltd.nullpointer.tcp.core.serialize.NPiotTCPMessageSerializer;
import ltd.nullpointer.tcp.core.serialize.MessageSerializerFactory;
import ltd.nullpointer.tcp.core.util.ClassPathPackageScanner;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 客户端测试
 *
 * @author zhangweilin
 * @ClassName: NPIotClient.java
 * @Description:
 * @date 2018年1月22日 上午10:43:40
 */
public abstract class NPIotClient extends Thread {
    protected AbstractClientListener abstractClientListener;
    private String ip;
    private int port;
    private Map<String, ClassFieldEntry> classFieldEntryMap;

    public NPIotClient(String ip, int port) {
        super();
        this.ip = ip;
        this.port = port;
        this.classFieldEntryMap = initAndCacheMsgCodeFieldListMap("com.boot2.npiot.api.bean.business");
    }

//	/**
//	 * 上线操作
//	 *
//	 * @param deviceId
//	 * @param online
//	 */
//	public void online(String deviceId, Online online) {
//		NPiotTCPMessage npiotTCPMessage = new NPiotTCPMessage();
//		npiotTCPMessage.setMsgSn(1);
//		npiotTCPMessage.setDeviceId(deviceId);
//		npiotTCPMessage.setPayload(online);
//		send(npiotTCPMessage);
//	}

    public void send(NPiotTCPMessage npiotTCPMessage) {
        if (null == abstractClientListener) {
            throw new IllegalStateException("please call setListner method first");
        }
        abstractClientListener.send(npiotTCPMessage);
    }

    public void send(ByteBuf byteBuf) {
        if (null == abstractClientListener) {
            throw new IllegalStateException("please call setListner method first");
        }
        abstractClientListener.send(byteBuf);
    }

    private Map<String, ClassFieldEntry> initAndCacheMsgCodeFieldListMap(String basePackage) {
        Map<String, ClassFieldEntry>msgCodeFieldListMap = new HashMap<>();
        Set<Class<?>> classSet = ClassPathPackageScanner.getClassesByPackageName(basePackage, TcpUpMessage.class);
        for (Class<?> class0 : classSet) {
            List<Field> fieldList = ReflectUtil.getAllFieldListWithParent(class0, "MSG_CODE", "msgCode");
//			MsgCode msgCode = ReflectUtil.readConstantProperty(Online.class, MsgCode.class, "MSG_CODE");
            TCPEnum.MsgCode msgCode = ReflectUtil.readConstantProperty(class0, "MSG_CODE");

            ClassFieldEntry classFieldEntry = new ClassFieldEntry();
            classFieldEntry.setClazz(class0);
            classFieldEntry.setFieldList(fieldList);
            classFieldEntryMap.put(msgCode.getMessageCode()+"", classFieldEntry);
        }
        return msgCodeFieldListMap;
    }

    public abstract void setListner(AbstractClientListener abstractClientListener);

    public void setNPiotTCPMessageResolver(NPiotTCPMessageResolver arkMessageResolver) {
        arkMessageResolver.setClassFieldEntryMap(classFieldEntryMap);
        MessageResolverFactory.getInstance().bindMessageAnalyzer(NPiotTCPMessage.ARK_HEADER_BYTE, arkMessageResolver);
    }

    public void setNPiotTCPMessageSerializer(NPiotTCPMessageSerializer npiotTCPMessageSerializer) {
        npiotTCPMessageSerializer.setClassFieldEntryMap(classFieldEntryMap);
        MessageSerializerFactory.getInstance().bindMessageAnalyzer(NPiotTCPMessage.class, npiotTCPMessageSerializer);
    }

    public void connect(String host, int port) {

        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    pipeline.addLast(new IdleStateHandler(300, 300, 600));
                    // the encoder and decoder are static as these are sharable
                    pipeline.addLast(new DeciphMessageDecoder());
                    pipeline.addLast(new EncryptMessageEncoder());
                    pipeline.addLast(new MessageDecoder());
                    pipeline.addLast(new MessageEncoder());
                    if (null == abstractClientListener) {
                        throw new Exception("there is no listner registed...");
                    }
                    pipeline.addLast(abstractClientListener);
                }
            });
            // 发起异步链接操作
            ChannelFuture channelFuture = bootstrap.connect(host, port).sync();

            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            // 关闭，释放线程资源
            group.shutdownGracefully();
        }
    }

    @Override
    public void run() {
        this.connect(ip, port);
    }

}